
package com.x52im.rbchat.logic.chat_guest.utils;

import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

import com.eva.android.widget.WidgetUtils;
import com.google.gson.Gson;
import com.x52im.rainbowchat.im.dto.QuoteMeta;
import com.x52im.rbchat.MyApplication;
//import com.x52im.rainbowchat.im.dto.MsgBody4Friend;
import com.x52im.rbchat.R;
import com.x52im.rainbowchat.common.dto.cnst.UserProtocalsType;
import com.x52im.rainbowchat.im.dto.MsgBody4Friend;
import com.x52im.rainbowchat.im.dto.MsgBody4Guest;
import com.x52im.rainbowchat.im.dto.RevokedMeta;
import com.x52im.rbchat.cache.AlarmsProvider;
import com.x52im.rbchat.logic.chat_root.model.ContactMeta;
import com.x52im.rbchat.logic.chat_root.model.FileMeta;
import com.x52im.rbchat.logic.chat_root.model.LocationMeta;
import com.x52im.rbchat.logic.chat_root.model.Message;
import com.x52im.rbchat.logic.chat_root.model.MessageExt;
import com.x52im.rbchat.network.im.SendDataHelper;

import net.x52im.mobileimsdk.server.protocal.Protocal;

import java.text.MessageFormat;
import java.util.Observable;
import java.util.Observer;

/**
 * 一对一陌生人聊天（或称为临时聊天）相关消息/指令的发送和解析方法。
 */
public class TMessageHelper
{
    private final static String TAG = TMessageHelper.class.getSimpleName();

    //*********************************************************************** （1）解析接收的消息或指令 START
	/**
	 * 解析临时聊天消息：由服务端转发给接收人B的【步骤2/2】.
	 * 
	 * <p>
	 * 当然，此消息被接收到的前提条件是B用户此时是在线的（否则临时聊天消息将服务端被存储到DB中（直到本地用户下次上线））。
	 * 
	 * @param originalMsg
	 * @return
	 */
	public static MsgBody4Guest parseTempChatMsg_SERVER$TO$B_Message(String originalMsg)
	{
		System.out.println("!!!!!!收到服务端发过来的临时聊天信息：" + originalMsg);
		return new Gson().fromJson(originalMsg, MsgBody4Guest.class);
	}
    //*********************************************************************** （1）解析接收的消息或指令 END


    //*********************************************************************** （2）发出的消息或指令(异步) START
    /**
     * 将指导定的图片消息发送给聊天中的陌生人（异步方式）.
     * <p>
     * 说明：安卓2.3及以后系统中规定：发送网络数据须在单独的线程中。
     * </p>
     *
     * @param tempChatFriendName 本参数用于已经加陌生人后的提示信息而已，本参数可为null哦（非必须）
     * @param message 文本消息，如果该文本为null或空字符串则不会真正执行发送过程
	 * @param quoteMeta 消息引用信息，此字段可为空（表示本条无引用消息）
     */
    public static void sendPlainTextMessageAsync(final Activity activity
            , final String tempChatUIDForInit
            , final String tempChatFriendName
            , final String message
			, final QuoteMeta quoteMeta
            , final Observer sucessObsExtra)
    {
        // 先生成指纹码
        final String fingerPrint = Protocal.genFingerPrint();
		// 消息指令通过网络发送成功后要通知的观察者
		Observer sendSucessObs = (o, arg) -> {
			// 消息数据对象（用于ui界面显示）
			Message m = MessageExt.createChatMsgEntity_OUTGO_TEXT(message, fingerPrint);
			// 尝试设置消息引用信息（当前仅针对文本消息有意义），以便用于ui显示
			m.setQuoteMeta(quoteMeta);

			// 消息发送成功后，将此消息数据放到聊天列表的数据模型中（让UI进行显示）
			TChatDataHelper.addChatMessageData_outgoing(activity, tempChatUIDForInit, m);

			// 消息发送调用者的额外要做的事
			if(sucessObsExtra != null)
				sucessObsExtra.update(null, m);
		};

		// 调用真正的消息指令发送方法
        sendMessageAsync(activity, MsgBody4Friend.TYPE_TEXT, tempChatUIDForInit, tempChatFriendName, message, fingerPrint, quoteMeta, sendSucessObs);
    }

    /**
     * 将指导定的图片消息发送给聊天中的陌生人（异步方式）.
     * <p>
     * 说明：安卓2.3及以后系统中规定：发送网络数据须在单独的线程中。
     * </p>
     *
     * @param activity
     * @param tempChatUIDForInit
    //	 * @param tempChatFriendName 本参数用于已经加陌生人后的提示信息而已，本参数可为null哦（非必须）
     * @param imageFilePath 文本消息，也即是图片的文件名，如果该文本为null或空字符串则不会真正执行发送过程
     * @param sucessObsExtra 消息发送成功后要通知的观察者，本参数可为null
     */
    public static void sendImageMessageAsync(final Activity activity
            , final String tempChatUIDForInit
            , final String tempChatFriendName
            , final String imageFilePath
            , final String fingerPring
            , final Observer sucessObsExtra)
    {
		// 消息指令通过网络发送成功后要通知的观察者
		Observer sendSucessObs = (o, arg) -> {
			// 发送图片消息成功后什么也不用做（其它在本异步线程执行前图片消息就已经放入到聊天列表了，这是与普通消息最大的区别哦）
			// do nothing

			// 消息发送调用者的额外要做的事
			if(sucessObsExtra != null)
				sucessObsExtra.update(null, null);
		};

		// 调用真正的消息指令发送方法
        sendMessageAsync(activity, MsgBody4Friend.TYPE_IMAGE, tempChatUIDForInit, tempChatFriendName
                , imageFilePath, fingerPring, sendSucessObs);
    }

    /**
     * 将指导定的语音消息发送给聊天中的陌生人（异步方式）.
     * <p>
     * 说明：安卓2.3及以后系统中规定：发送网络数据须在单独的线程中。
     * </p>
     *
     * @param activity
     * @param tempChatUIDForInit  对方的uid
     * @param tempChatFriendName 本参数用于已经加陌生人后的提示信息而已，本参数可为null哦（非必须）
     * @param voiceFileName 要发送的语音留言录音文件名
     * @param sucessObsExtra 消息发送成功后要通知的观察者，本参数可为null
     */
    public static void sendVoiceMessageAsync(final Activity activity
            , final String tempChatUIDForInit
            , final String tempChatFriendName
            , final String voiceFileName
            , final String fingerPring
            , final Observer sucessObsExtra)
    {
		// 消息指令通过网络发送成功后要通知的观察者
		Observer sendSucessObs = (o, arg) -> {
			// 发送语音消息成功后什么也不用做（其它在本异步线程执行前语音消息就已经放入到聊天列表了，这是与普通消息最大的区别哦）
			// do nothing

			// 消息发送调用者的额外要做的事
			if(sucessObsExtra != null)
				sucessObsExtra.update(null, null);
		};

		// 调用真正的消息指令发送方法
        sendMessageAsync(activity, MsgBody4Friend.TYPE_VOICE, tempChatUIDForInit, tempChatFriendName
                , voiceFileName, fingerPring, sendSucessObs);
    }

	/**
	 * 将指定的文件消息发送给聊天中的陌生人（异步方式）.
	 * <p>
	 * 说明：安卓2.3及以后系统中规定：发送网络数据须在单独的线程中。
	 * </p>
	 *
	 * @param content 文件消息的内容就是FileMeta对象
	 * @return code:0 表示发送成功，否则错误码
	 * @see #sendMessageAsync(Activity, int, String, String, String, String, Observer)
	 * @since 5.1
	 */
	public static void sendFileMessageAsync(final Activity activity
			, final String tempChatUIDForInit, final String tempChatFriendName
			, final FileMeta content, final String fingerPring
			, final Observer sucessObsExtra)
	{
		// 消息指令通过网络发送成功后要通知的观察者
		Observer sendSucessObs = (o, arg) -> {
			// 发送文件消息成功后什么也不用做（其它在本异步线程执行前语音消息就已经放入到聊天列表了，这是与普通消息最大的区别哦）
			// do nothing

			// 消息发送调用者的额外要做的事
			if(sucessObsExtra != null)
				sucessObsExtra.update(null, null);
		};

		// 调用真正的消息指令发送方法
		sendMessageAsync(activity, MsgBody4Friend.TYPE_FILE, tempChatUIDForInit, tempChatFriendName
				, new Gson().toJson(content), fingerPring, sendSucessObs);
	}

	/**
	 * 将指定的短视频消息发送给聊天中的陌生人（异步方式）.
	 * <p>
	 * 说明：安卓2.3及以后系统中规定：发送网络数据须在单独的线程中。
	 * </p>
	 *
	 * @param content 短视频消息的内容就是FileMeta对象
	 * @return code:0 表示发送成功，否则错误码
	 * @see #sendMessageAsync(Activity, int, String, String, String, String, Observer)
	 * @since 5.1
	 */
	public static void sendShortVideoMessageAsync(final Activity activity
			, final String tempChatUIDForInit, final String tempChatFriendName
			, final FileMeta content, final String fingerPring, final Observer sucessObsExtra)
	{
		// 消息指令通过网络发送成功后要通知的观察者
		Observer sendSucessObs = (o, arg) -> {

			// 发送短视频消息成功后什么也不用做（其它在本异步线程执行前语音消息就已经放入到聊天列表了，这是与普通消息最大的区别哦）
			// do nothing

			// 消息发送调用者的额外要做的事
			if(sucessObsExtra != null)
				sucessObsExtra.update(null, null);
		};

		// 调用真正的消息指令发送方法
		sendMessageAsync(activity, MsgBody4Friend.TYPE_SHORTVIDEO, tempChatUIDForInit
				, tempChatFriendName, new Gson().toJson(content), fingerPring, sendSucessObs);
	}

	/**
	 * 将指定的个人名片消息发送给聊天中的陌生人（异步方式）.
	 * <p>
	 * 说明：安卓2.3及以后系统中规定：发送网络数据须在单独的线程中。
	 * </p>
	 *
	 * @param content 文件消息的内容就是FileMeta对象
	 * @return code:0 表示发送成功，否则错误码
	 * @see #sendMessageAsync(Activity, int, String, String, String, String, Observer)
	 * @since 4.3
	 */
	public static void sendContactMessageAsync(final Activity activity
			, final String tempChatUIDForInit, final String tempChatFriendName
			, final ContactMeta content, final Observer sucessObsExtra)
	{
		// 先生成指纹码
		final String fingerPring = Protocal.genFingerPrint();

		// 消息指令通过网络发送成功后要通知的观察者
		Observer sendSucessObs = (o, arg) -> {
			// 消息发送成功后，将此消息数据放到聊天列表的数据模型中（让UI进行显示）
			Message m = TChatDataHelper.addChatMessageData_outgoing(activity, tempChatUIDForInit
					, MessageExt.createChatMsgEntity_OUTGO_CONTACT(content, fingerPring));

			// 消息发送调用者的额外要做的事
			if(sucessObsExtra != null)
				sucessObsExtra.update(null, m);
		};

		// 调用真正的消息指令发送方法
		sendMessageAsync(activity, MsgBody4Friend.TYPE_CONTACT, tempChatUIDForInit
				, tempChatFriendName, new Gson().toJson(content), fingerPring, sendSucessObs);
	}

	/**
	 * 将指定的位置消息发送给聊天中的陌生人（异步方式）.
	 * <p>
	 * 说明：安卓2.3及以后系统中规定：发送网络数据须在单独的线程中。
	 * </p>
	 *
	 * @param content 文件消息的内容就是LocationMeta对象
	 * @return code:0 表示发送成功，否则错误码
	 * @see #sendMessageAsync(Activity, int, String, String, String, String, Observer)
	 * @since 6.0
	 */
	public static void sendLocationMessageAsync(final Activity activity
			, final String tempChatUIDForInit, final String tempChatFriendName
			, final LocationMeta content, final Observer sucessObsExtra)
	{
		// 先生成指纹码
		final String fingerPring = Protocal.genFingerPrint();

		// 消息指令通过网络发送成功后要通知的观察者
		Observer sendSucessObs = (o, arg) -> {
			// 消息发送成功后，将此消息数据放到聊天列表的数据模型中（让UI进行显示）
			Message m = TChatDataHelper.addChatMessageData_outgoing(activity, tempChatUIDForInit
					, MessageExt.createChatMsgEntity_OUTGO_LOCATION(content, fingerPring));

			// 消息发送调用者的额外要做的事
			if(sucessObsExtra != null)
				sucessObsExtra.update(null, m);
		};

		// 调用真正的消息指令发送方法
		sendMessageAsync(activity, MsgBody4Friend.TYPE_LOCATION, tempChatUIDForInit
				, tempChatFriendName, new Gson().toJson(content), fingerPring, sendSucessObs);
	}

	/**
	 * "撤回"消息（异步方式）.
	 *
	 * @param content 消息撤回指令的内容就是RevokedMeta对象
	 * @return 返回"撤回"指令自已的指纹码
	 * @see #sendMessageAsync(Activity, int, String, String, String, String, Observer)
	 */
	public static void sendRevokeMessageAsync(final Activity activity
			, String fingerPrint, final String tempChatUIDForInit, final String tempChatFriendName
			, final RevokedMeta content, final Observer sucessObsExtra)
	{
		// 消息指令通过网络发送成功后要通知的观察者
		Observer sendSucessObs = (o, arg) -> {
			// 注意：由于消息"撤回"指令需要等对方的应答回来（也就是对方收到撤回指令后）才能做本地的真正"撤回"逻辑，
			//      所以此观察者中无法立即进行"撤回"处理，余下的本地撤回逻辑将由 MessageRevoingManager来实现！

			// 消息发送调用者的额外要做的事
			if(sucessObsExtra != null)
				sucessObsExtra.update(null, null);
		};

		// 调用真正的消息指令发送方法
		sendMessageAsync(activity, MsgBody4Friend.TYPE_REVOKE, tempChatUIDForInit
			, tempChatFriendName, new Gson().toJson(content), fingerPrint, sendSucessObs);
	}

	/**
	 * 将指定的消息发送给聊天中的陌生人（异步方式）.
	 * <p>
	 * 说明：安卓2.3及以后系统中规定：发送网络数据须在单独的线程中。
	 * </p>
	 *
	 * @param messageType 参见  {@link MsgBody4Friend}中的文本消息类型
	 * @param tempChatFriendName 本参数用于已经加好友人后的提示信息而已，本参数可为null哦（非必须）
	 * @param message 文本消息，如果该文本为null或空字符串则不会真正执行发送过程
	 */
	private static void sendMessageAsync(final Activity activity
			, final int messageType
			, final String tempChatFriendUID
			, final String tempChatFriendName // 本参数目前有两个用途：用于提示已成为正式好友时、用于本地发送消息时显示的首页消息页里
			, final String message
			, final String fingerPrint
			, Observer sucessObs)
	{
		TMessageHelper.sendMessageAsync(activity, messageType, tempChatFriendUID, tempChatFriendName, message, fingerPrint, null, sucessObs);
	}

    /**
     * 将指定的消息发送给聊天中的陌生人（异步方式）.
     * <p>
     * 说明：安卓2.3及以后系统中规定：发送网络数据须在单独的线程中。
     * </p>
     *
     * @param messageType 参见  {@link MsgBody4Friend}中的文本消息类型
     * @param tempChatFriendName 本参数用于已经加好友人后的提示信息而已，本参数可为null哦（非必须）
     * @param message 文本消息，如果该文本为null或空字符串则不会真正执行发送过程
	 * @param quoteMeta 消息引用信息（当前仅用于文本消息时），此字段可为空（表示本条无引用消息）
     */
    private static void sendMessageAsync(final Activity activity
            , final int messageType
            , final String tempChatFriendUID
            , final String tempChatFriendName // 本参数目前有两个用途：用于提示已成为正式好友时、用于本地发送消息时显示的首页消息页里
            , final String message
            , final String fingerPrint
			, final QuoteMeta quoteMeta
            , Observer sucessObs)
    {
        if (message != null && message.length() > 0)
        {
            // 如果与该陌生人已经是好友了，就提示当前聊天用户进入正式聊天界面中（再行聊天）
            if(MyApplication.getInstance(activity)
                    .getIMClientManager().getFriendsListProvider().isUserInRoster(tempChatFriendUID))
            {
                WidgetUtils.showToastLong(activity
                        , MessageFormat.format(activity.getString(R.string.temp_chat_has_already_be_friend)
                                , tempChatFriendName == null ?"He/She":tempChatFriendName)
                        , WidgetUtils.ToastType.WARN);
                return;
            }

            // 新聊天框架中的网络数据发送需要放到异步线程里（Android2.3以上版本要求网络交互不允许发生在主线程里）
            new AsyncTask<Object, Integer, Integer>(){
                // 数据成功发出去后要通知的观察者（目前，新聊天框架在有丢包重传机前，“成功发出”只意味着发出去但不意味着一定能被收到）
                private Observer sucessObs = null;

                @Override
                protected Integer doInBackground(Object... params) {
                    if(params != null)
                        sucessObs = (Observer)params[0];

					MsgBody4Guest tcmd = constructTempChatMsgDTOForSend(activity, messageType, tempChatFriendUID, message);
					// 尝试设置消息引用信息
					tcmd.setQuoteMeta(quoteMeta);
					return TMessageHelper.sendTempChatMsg_A$TO$SERVER_Message(activity, tcmd, true, fingerPrint);
//                    return TMessageHelper.sendChatMessage(activity, messageType, tempChatFriendUID, message, fingerPrint);
                }

                @Override
                protected void onPostExecute(Integer code) {
                    // 消息发送成功
                    if(code == 0) {
                        if(sucessObs != null)
                            sucessObs.update(null, null);

                        // 将本地用户主动发出的临时聊天消息也入到首页消息栏里.
                        // * <p>
                        // * 2.2版之前，首页消息栏只在收到消息时才会放入，但像微信这样的IM里，
                        // * 为了方便下次查看，自已主动发的消息也放到了首页消息栏（而不限于收到的消息），
                        // * 自已发的消息放到首页消息栏仅仅是为了方便，别无他用。
                        AlarmsProvider.addATempChatMsgAlarmForLocal(activity, messageType, tempChatFriendUID, tempChatFriendName, message);
                    } else {
                        Log.e(TAG, "网络发送数据失败，错误码：code="+code);
                        WidgetUtils.showToast(activity
                                , activity.getString(R.string.chat_message_send_error), WidgetUtils.ToastType.ERROR);
                    }
                }
            }.execute(sucessObs);
        }
    }
    //*********************************************************************** （2）发出的消息或指令(异步) END


    //*********************************************************************** （3）发出的消息或指令(同步) START
//	/**
//	 * 发送聊天消息（包括普通文本、图片消息、语音留言消息等）给指定user_id的用户.
//	 * <b>注意：</b>目前普通文本消息为了提升用户体验，提供QoS支持.
//     * <p>
//     * 说明：安卓2.3及以后系统中规定：发送网络数据须在单独的线程中。
//     * </p>
//	 *
//	 * @param context
//	 * @param msg 消息内容，纯文本字串，可能是聊天文字、图片文件名或语音文件名等，但一定不是JSON字串
//	 * @return
//	 * @throws Exception
//	 */
//	public static int sendChatMessage(Context context
//			, int msgType, String friendUid, String msg, String fingerPrint)
//	{
//		return sendTempChatMsg_A$TO$SERVER_Message(context, msgType, friendUid, msg, true, fingerPrint);
//	}

//	/**
//	 * 发送临时聊天消息：由发送人A发给服务端【步骤1/2】.
//     * <p>
//     * 说明：安卓2.3及以后系统中规定：发送网络数据须在单独的线程中。
//     * </p>
//	 *
//	 * @param context
//	//* @param jsonStrOfTextMessage 消息内容（TextMessage对象的JSON文本）
//	 * @param msg 消息内容，纯文本字串，可能是聊天文字、图片文件名或语音文件名等，但一定不是JSON字串
//	 * @return
//	 * @throws Exception
//	 */
//	private static int sendTempChatMsg_A$TO$SERVER_Message(Context context
//			, int msgType, String friendUid, String msg, boolean QoS, String fingerPrint)
//	{
//		MsgBody4Guest tcmd = constructTempChatMsgDTOForSend(context, msgType, friendUid, msg);
//		// 发送消息时，user_id=0即表示发送给服务端
//		return sendTempChatMsg_A$TO$SERVER_Message(context, tcmd, QoS, fingerPrint);
//	}

	/**
	 * 发送临时聊天消息：由发送人A发给服务端【步骤1/2】.
     * <p>
     * 说明：安卓2.3及以后系统中规定：发送网络数据须在单独的线程中，本方法默认没有为开发
     * 者提供异步实现，所以开发者调用时需单独在代码中使用AsyncTask包装一遍哦。
     * </p>
	 * 
	 * @param context
	 * @param tcmd
	 * @return
	 * @throws Exception
	 */
	private static int sendTempChatMsg_A$TO$SERVER_Message(Context context, MsgBody4Guest tcmd, boolean QoS, String fingerPrint)
	{
		// 发送消息时，user_id=0即表示发送给服务端
		// 【说明：】自rbchat2.2(2014-02-12)起，临时聊天也启用了QoS机制
		return SendDataHelper.sendMessageImpl(context, "0", (new Gson().toJson(tcmd))
				, QoS, fingerPrint, UserProtocalsType.MT42_OF_TEMP$CHAT$MSG_A$TO$SERVER);
	}
    //*********************************************************************** （3）发出的消息或指令(同步) END


	/**
	 * 构造临时聊天DTO对象.
	 * 
	 * @param context
	 * @param friendUid
	//* @param jsonStrOfTextMessage 消息内容（TextMessage对象的JSON文本）
	 * @param msg 消息内容，纯文本字串，可能是聊天文字、图片文件名或语音文件名等，但一定不是JSON字串
	 * @return
	 */
	private static MsgBody4Guest constructTempChatMsgDTOForSend(Context context, int msgType, String friendUid, String msg)
	{
	    return MsgBody4Guest.constructGuestChatMsgBody(
	            msgType
                , MyApplication.getInstance(context).getIMClientManager().getLocalUserInfo().getUser_uid()
                , MyApplication.getInstance(context).getIMClientManager().getLocalUserInfo().getNickname()
                , friendUid
                , msg);
	}

	/**
	 * 构造临时聊天DTO对象.
	 *
	 * @param srcUserUid 发送方的uid
	 * @param srcNickName 发送方的昵称
	 * @param friendUid 接收方的uid
	 //* @param jsonStrOfTextMessage 消息内容（TextMessage对象的JSON文本）
	 * @param msg 消息内容，纯文本字串，可能是聊天文字、图片文件名或语音文件名等，但一定不是JSON字串
	 * @return
	 */
	public static MsgBody4Guest constructTempChatMsgDTO(
			int msgType, String srcUserUid, String srcNickName, String friendUid, String msg)
	{
        return MsgBody4Guest.constructGuestChatMsgBody(msgType, srcUserUid, srcNickName, friendUid, msg);
	}

}
